#pragma once

#include <windows.h>
#include <cstdio>
#include <Common/Base/hkBase.h>
#include <Common/Base/Container/String/hkStringBuf.h>
#include <Common/Base/Ext/hkBaseExt.h>
#include <Common/Visualize/hkDebugDisplay.h>

#include <Physics/Collide/Shape/Convex/Box/hkpBoxShape.h>
#include <Physics/Collide/Shape/HeightField/SampledHeightField/hkpSampledHeightFieldShape.h>
#include <Physics/Collide/Shape/HeightField/SampledHeightField/hkpSampledHeightFieldBaseCinfo.h>
#include <Common/Base/Types/Geometry/Aabb/hkAabb.h>
#include <Common/Base/Algorithm/PseudoRandom/hkPseudoRandomGenerator.h>
#include <Physics/Collide/Query/CastUtil/hkpWorldRayCastInput.h>
#include <Physics/Collide/Query/CastUtil/hkpWorldRayCastOutput.h>
#include <Physics/Collide/Query/Collector/RayCollector/hkpClosestRayHitCollector.h>

#include <Physics/Collide/Filter/Group/hkpGroupFilter.h>
#include <Physics/Collide/Filter/Group/hkpGroupFilterSetup.h>

#include <Physics/Collide/Shape/Convex/Box/hkpBoxShape.h>
#include <Physics/Collide/Shape/Convex/Sphere/hkpSphereShape.h>
#include <Physics/Collide/Shape/Convex/Triangle/hkpTriangleShape.h>
#include <Physics/Collide/Shape/Convex/ConvexVertices/hkpConvexVerticesShape.h>
#include <Physics/Collide/Shape/Compound/Tree/Mopp/hkpMoppBvTreeShape.h>

#include <Physics/Collide/Dispatch/hkpAgentRegisterUtil.h>
#include <Physics/Utilities/Dynamics/Inertia/hkpInertiaTensorComputer.h>
#include <Physics/Utilities/Actions/MouseSpring/hkpMouseSpringAction.h>
#include <Physics/Utilities/Dynamics/PhysicsBridge/hkpPhysicsBridge.h>

#include <Physics/Dynamics/World/hkpWorld.h>
#include <Physics/Dynamics/Entity/hkpRigidBody.h>

#include <Demos\DemoCommon\Utilities\Noise\PerlinNoise.h>
#include <Common/Base/KeyCode.h> // needed for the HK_FEATURE... to work with the reflection script
#include <Common/Base/System/Io/Socket/hkSocket.h>
#include <Common/Base/Object/hkSingleton.h>
#include <Common/Base/DebugUtil/DeterminismUtil/hkCheckDeterminismUtil.h>
#include <Common/Base/Memory/System/hkMemorySystem.h>
#include <Common/Base/Thread/Job/ThreadPool/hkJobThreadPool.h>
#include <Common/Base/Thread/JobQueue/hkJobQueue.h>
#include <Common/Base/DebugUtil/MemoryExceptionTestingUtil/hkMemoryExceptionTestingUtil.h>
#include <Common/Base/System/hkBaseSystem.h>
#include <Physics/Utilities/VisualDebugger/hkpPhysicsContext.h>
#include <Common/Base/Thread/Job/ThreadPool/Cpu/hkCpuJobThreadPool.h>
#include <Physics/Dynamics/World/hkpWorldCinfo.h>
#include <Common\Base\Memory\System\Util\hkMemoryInitUtil.h>
#include <Common\Base\Memory\Allocator\Malloc\hkMallocAllocator.h>

class CoarseSampledHeightFieldShape: public hkpSampledHeightFieldShape
{
public:
	HK_DECLARE_CLASS_ALLOCATOR(HK_MEMORY_CLASS_DEMO);
	CoarseSampledHeightFieldShape( const hkpSampledHeightFieldBaseCinfo& ci, hkUint16* data )
		:	hkpSampledHeightFieldShape(ci),
		m_data(data)
	{
	}

	// Generate a rough terrain
	HK_FORCE_INLINE hkReal getHeightAtImpl( int x, int z ) const
	{
		// Lookup data and return a float
		// We scale the data artificially by 20.0f to make it look interesting
		return 20.0f * hkReal(m_data[x * m_zRes + z]) / hkReal( hkUint16(-1) );
	}

	// Assuming each heightfield quad is defined as four points { 00, 01, 11, 10 },
	// this should return true if the two triangles share the edge p00-p11.
	// Otherwise it should return false if the triangles share the edge p01-p10
	HK_FORCE_INLINE hkBool getTriangleFlipImpl() const
	{	
		return false;
	}

	virtual void collideSpheres( const CollideSpheresInput& input, SphereCollisionOutput* outputArray) const
	{
		hkSampledHeightFieldShape_collideSpheres(*this, input, outputArray);
	}

private:
	hkUint16* m_data;
};

static void HK_CALL errorReportFunction(const char* str, void*)
{
	OutputDebugStringA(str);
	OutputDebugStringA("\n");

	printf("%s\n", str);
}

class HavokSystem
{
public:
	HavokSystem()
		:m_physicsStepCounter(0), m_heightData(0), m_timestep(0), 
		m_heightFieldShape(0), m_jobQueue(0), m_jobThreadPool(0), m_renderParallelWithSimulation(0)
	{
	}

	int	m_physicsStepCounter;
	hkpWorld* m_world;
	hkUint16* m_heightData;
	CoarseSampledHeightFieldShape* m_heightFieldShape;

	// Multi-threading data
	class hkJobQueue* m_jobQueue;
	class hkJobThreadPool* m_jobThreadPool;
	hkMemoryRouter* memoryRouter;
	// If set to false, the user cannot reduce the number of active SPUs to zero using the demo framework.
	// This is necessary for e.g. the collision query demos to avoid a situation where an SPU job remains on
	// the job queue if no active SPU is available and the PPU is not allowed to take it.
	hkBool m_allowZeroActiveSpus;
	hkBool m_allowChangingNumThreads;
	hkBool m_forcedShadowsOff;
	hkBool m_forcedShadowsOn;
	hkBool m_forcedDebugShadowMap;
	bool   m_simulationStarted;
	hkBool m_renderParallelWithSimulation;
	hkReal m_timestep;
	hkpPhysicsContext* m_physicsViewersContext;
	hkpRigidBody* m_sphere, *m_cube;
	void addOrRemoveThreads();
	bool frameworkInitBaseSystem( hkMemoryAllocator* base );
	void CreateWorld();	
	void startNewSimulateStep();
	int  Step();
	void CreateGround(int xRes, int zRes);
};
